Skip to content

playwright(fix): test reliability — ES indexing, locator strict-mode, waitForResponse races#29585

Open
chirag-madlani wants to merge 10 commits into
mainfrom
playwright-test-reliability-fixes
Open

playwright(fix): test reliability — ES indexing, locator strict-mode, waitForResponse races#29585
chirag-madlani wants to merge 10 commits into
mainfrom
playwright-test-reliability-fixes

Conversation

@chirag-madlani

@chirag-madlani chirag-madlani commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Describe your changes:

PR-A of a 4-way split of #29435. This PR contains only the test-side bug fixes — no config changes, no spec-file moves, no sharding shift. Each fix addresses a real race or locator issue in an existing Playwright E2E spec that was previously surviving on retries:2 but became visible under retry pressure.

The fixes fall into 4 categories:

ES indexing races — entity created via API, then read by UI before search index updates:

  • BulkEditEntity.spec.ts: wait for glossary_term_search_index
  • BulkEditOperationBadges.spec.ts: same pattern in beforeAll
  • BulkImport.spec.ts: wait for database_service / database_schema / table indexes; pre-wait .rdg-cell-details count before asserting text
  • TestCaseImportExportE2eFlow.spec.ts: wait for test_case_search_index so the just-created test case is in the export CSV

waitForResponse races — listener registered after the action, or matching a too-broad URL that resolves on a stale earlier response:

  • utils/searchRBAC.ts searchForEntity helpers: register before press('Enter')
  • SearchIndexNestedColumns.spec.ts: predicate-match the URL containing the encoded search term

Locator stability / timeout fixes:

  • Roles.spec.ts (×7 sites): replace strict-mode-violating expect(loader).not.toBeVisible() with waitForAllLoadersToDisappear (handles N loaders correctly)
  • ExploreBrowse.spec.ts: explicit toBeVisible before clicking tree title so the tree settles after expandTreeNode
  • Table.spec.ts: wait for sort + next-page responses, replace count()===15 with toHaveCount(15) auto-retry

Backend-propagation timeouts — UI fetches lag the API write:

  • utils/searchRBAC.ts exploreTreeCategories: wrap visibility/absence checks in expect.toPass({ timeout: 20s }) — tree counts come from multiple aggregation queries
  • utils/searchRBAC.ts exploreShouldShowEntity: bump negative-assertion to 45s — RBAC filtering against newly-assigned roles lags the patch
  • utils/taskWorkflow.ts openTaskDetails: 15s → 45s — activity-feed refresh after API task create lags past default under load

Also removed 7 unannotated waitForTimeout calls leaking past ESLint, replaced with proper waits. The 4 in DomainUIInteractions / DataProductAndSubdomains migrated to the existing waitForSearchIndexed polling helper.

Type of change:

  • Bug fix (test-side)

High-level design:

Each fix is independent and self-contained. No shared helpers introduced; just makes existing tests' assertions deterministic against the system they're already testing. The PR is a clean 16-file diff (+214 / -49) with no file moves, deletes, or new tests.

Tests:

Pure test-side fixes. No new tests needed — each change makes an existing flaky assertion deterministic. Verified locally that prettier + organize-imports pass and that tsc --noEmit introduces no new errors (pre-existing TS errors on main are unaffected).

Playwright (UI) tests

  • Not applicable for adding tests — existing tests are made more robust.

UI screen recording / screenshots:

Not applicable — no UI changes.

Checklist:

  • I have read the CONTRIBUTING document.
  • I have commented on my code, particularly in hard-to-understand areas.

Split off from #29435 — this is the first of four progressively-merging PRs (test fixes → single-spec caps → config + dedup → PR/stress project split). No associated GitHub issue — this is internal test-tooling work.

🤖 Generated with Claude Code

Greptile Summary

This PR makes 16 Playwright E2E spec files more reliable by replacing waitForTimeout calls and flaky assertion patterns with proper async synchronization primitives — waitForSearchIndexed polling, waitForResponse predicates, toHaveCount / toPass, and explicit toBeVisible guards before clicks.

  • ES indexing races fixed in five spec files (BulkEditEntity, BulkEditOperationBadges, BulkImport, TestCaseImportExportE2eFlow, and the domain/subdomain specs) by inserting waitForSearchIndexed calls after API entity creation.
  • waitForResponse races fixed in searchForEntityShouldWork and searchForEntityShouldWorkShowNoResult by registering the listener between fill and press('Enter'); SearchIndexNestedColumns updated to match by URL predicate instead of a broad glob.
  • Locator/count assertion improvements in Table.spec.ts (count() === NtoHaveCount(N)), ExploreBrowse.spec.ts (explicit toBeVisible before tree-node click), and taskWorkflow.ts (15 s → 45 s timeout on openTaskDetails).

Confidence Score: 4/5

exploreShouldShowEntity still registers its waitForResponse listener before fill(), leaving it exposed to the autocomplete-response race that was explicitly fixed in the two sibling helpers in this same PR. All other changes are correct.

exploreShouldShowEntity registers searchRes = page.waitForResponse('/api/v1/search/query?*') before fill(fqn), so an autocomplete query fired while the user types can consume the listener before press('Enter') fires the real search — assertions then run against stale autocomplete UI. This is the identical race that searchForEntityShouldWork and searchForEntityShouldWorkShowNoResult were fixed for in this very PR, but the fix was not carried over here.

searchRBAC.tsexploreShouldShowEntity (lines 48–51) needs the same fill-then-register-then-press ordering applied to the other two helpers in this file.

Important Files Changed

Filename Overview
openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts searchForEntityShouldWork and searchForEntityShouldWorkShowNoResult correctly register waitForResponse after fill and before press('Enter'), but exploreShouldShowEntity still registers searchRes before fill() — the same race this PR set out to fix.
openmetadata-ui/src/main/resources/ui/playwright/utils/polling.ts Adds an early-exit guard for undefined FQN, preventing the empty q= match-all footgun; change is correct and well-commented.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/SearchIndexNestedColumns.spec.ts Name-search wait replaced with element waitFor on the specific suggestion testId; column-search listener upgraded to URL-predicate match — both fixes are sound.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/Table.spec.ts Replaces brittle count() === 15 with toHaveCount(15) and adds waitForResponse guards before sort/next-page clicks; straightforward improvement.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BulkImport.spec.ts Adds waitForSearchIndexed for database service, schema, and table before bulk-import grid interactions, plus a toHaveCount pre-wait before toContainText on result cells.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/ExploreBrowse.spec.ts Adds toBeVisible poll before tree-node click and registers waitForResponse before each click — correctly fixes tree-animation timing issue.
openmetadata-ui/src/main/resources/ui/playwright/utils/taskWorkflow.ts Bumps openTaskDetails visibility timeout from 15 s to 45 s with clear comment; reasonable adjustment for feed-propagation lag under parallelism.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/TestCaseImportExportE2eFlow.spec.ts Adds waitForSearchIndexed for the just-created test case in both Admin and non-Admin beforeAll blocks before the export step reads from search.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DomainUIInteractions.spec.ts Replaces three waitForTimeout(2000) calls in retry loops with waitForSearchIndexed polling; the .catch(() => undefined) suppression is intentional (soft retry).
openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataProductAndSubdomains.spec.ts Same pattern as DomainUIInteractions — replaces waitForTimeout in retry loop with waitForSearchIndexed; consistent and correct.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant T as Test
    participant PW as Playwright
    participant UI as Browser UI
    participant API as Search API (ES)

    Note over T,API: searchForEntityShouldWork / searchForEntityShouldWorkShowNoResult (FIXED)
    T->>UI: fill(fqn)
    UI->>API: "autocomplete query /api/v1/search/query?q=..."
    API-->>UI: autocomplete response (ignored — listener not yet registered)
    T->>PW: waitForResponse registered HERE
    T->>UI: press('Enter')
    UI->>API: "search-results query /api/v1/search/query?q=..."
    API-->>PW: response captured ✓
    PW-->>T: searchResponse resolved
    T->>UI: assert resultCard

    Note over T,API: exploreShouldShowEntity (STILL UNFIXED in this PR)
    T->>PW: waitForResponse registered TOO EARLY ⚠
    T->>UI: fill(fqn)
    UI->>API: "autocomplete query /api/v1/search/query?q=..."
    API-->>PW: autocomplete response resolves searchRes prematurely ⚠
    T->>UI: press('Enter')
    UI->>API: search-results query (listener already consumed)
    T->>UI: assert resultCard — runs against autocomplete state
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant T as Test
    participant PW as Playwright
    participant UI as Browser UI
    participant API as Search API (ES)

    Note over T,API: searchForEntityShouldWork / searchForEntityShouldWorkShowNoResult (FIXED)
    T->>UI: fill(fqn)
    UI->>API: "autocomplete query /api/v1/search/query?q=..."
    API-->>UI: autocomplete response (ignored — listener not yet registered)
    T->>PW: waitForResponse registered HERE
    T->>UI: press('Enter')
    UI->>API: "search-results query /api/v1/search/query?q=..."
    API-->>PW: response captured ✓
    PW-->>T: searchResponse resolved
    T->>UI: assert resultCard

    Note over T,API: exploreShouldShowEntity (STILL UNFIXED in this PR)
    T->>PW: waitForResponse registered TOO EARLY ⚠
    T->>UI: fill(fqn)
    UI->>API: "autocomplete query /api/v1/search/query?q=..."
    API-->>PW: autocomplete response resolves searchRes prematurely ⚠
    T->>UI: press('Enter')
    UI->>API: search-results query (listener already consumed)
    T->>UI: assert resultCard — runs against autocomplete state
Loading

Comments Outside Diff (6)

  1. openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)

    P2 waitForResponse still registered before fill() in exploreShouldShowEntity

    searchForEntityShouldWork and searchForEntityShouldWorkShowNoResult were fixed in this PR to register the listener between fill and press('Enter') specifically because fill can trigger an autocomplete/suggestion query that resolves the broad waitForResponse('/api/v1/search/query?*') listener prematurely. exploreShouldShowEntity still registers searchRes before fill() on line 48, leaving it exposed to the same race: if the search box fires a suggestion fetch while the user is typing, searchRes resolves on that response and press('Enter')'s actual result page is never waited for, so the resultCard assertion can run against stale UI.

  2. openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)

    P1 waitForResponse still registered before fill() in exploreShouldShowEntity

    searchRes is registered before fill(fqn) on line 48. If the search box fires an autocomplete/suggestion query while the user is typing, searchRes resolves on that early response and the press('Enter') result page is never awaited — so the resultCard assertion runs against stale UI. This is the exact race that was deliberately fixed in searchForEntityShouldWork (register after fill, before press) but the same pattern was left unremedied here.

  3. openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)

    P1 searchRes is registered before fill(fqn) (line 49), so if the search box fires an autocomplete/suggestion query while the user types, that early response resolves the listener and the press('Enter') result-page response is never awaited — the resultCard assertion then runs against stale UI. This is exactly the race fixed in searchForEntityShouldWork and searchForEntityShouldWorkShowNoResult in this same PR: register the listener after fill, before press.

  4. openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)

    P1 searchRes is registered before fill(fqn) on line 48. The fill() call on line 49 can immediately trigger an autocomplete/suggestion query whose URL matches '/api/v1/search/query?*', resolving the listener before press('Enter') fires the actual results-page query. The assertion then runs against stale autocomplete results rather than the real search page. This is the exact race that was deliberately fixed in searchForEntityShouldWork and searchForEntityShouldWorkShowNoResult in this same PR — register after fill, before press.

  5. openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)

    P1 waitForResponse registered before fill() — same race left unfixed

    searchRes (line 48) is created before fill(fqn) executes on line 49. Typing into the search box triggers autocomplete queries whose URL matches '/api/v1/search/query?*', which can resolve searchRes on that autocomplete response before press('Enter') fires the actual search-page query. When that happens the waitForAllLoadersToDisappear and the resultCard assertion run against the autocomplete-populated UI, not the search-results page — so toHaveCount(0, { timeout: 45_000 }) for the negative path may pass or fail spuriously.

    searchForEntityShouldWork and searchForEntityShouldWorkShowNoResult were fixed in this very PR to move the listener registration to after fill and before press('Enter') for exactly this reason. The same fix is needed here.

  6. openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)

    P1 searchRes is registered before fill(fqn) on line 48. Typing into the search box triggers autocomplete/suggestion queries whose URL matches '/api/v1/search/query?*', which resolves searchRes on that early response before press('Enter') fires the actual search-results-page query. The waitForAllLoadersToDisappear and resultCard assertions then run against stale autocomplete UI — toHaveCount(0, { timeout: 45_000 }) on the negative path can pass or fail spuriously.

    This is the identical race that was deliberately fixed in searchForEntityShouldWork and searchForEntityShouldWorkShowNoResult in this same PR (register after fill, before press). The fix should be applied here too.

Reviews (8): Last reviewed commit: "Merge branch 'main' into playwright-test..." | Re-trigger Greptile

… waitForResponse races

Bundle of test-side bug fixes for races and locator issues in existing
Playwright E2E specs. No config, no test-file moves, no behavior change
to the system under test — just makes flaky assertions deterministic.

ES indexing races (entities created via API, then read by UI before
search index updates):
- BulkEditEntity.spec.ts: wait for glossary_term_search_index before
  opening bulk-edit table (otherwise the term shows as "Entity created"
  instead of "Entity updated")
- BulkEditOperationBadges.spec.ts: same pattern for the file's beforeAll
  (term + table search indexes)
- BulkImport.spec.ts: wait for database_service / database_schema /
  table search indexes; pre-wait .rdg-cell-details rendering before
  asserting text (was racing the async grid populate)
- TestCaseImportExportE2eFlow.spec.ts: wait for test_case_search_index
  so the just-created test case appears in the export's CSV

waitForResponse races (listener registered after the action, or matching
too-broad URL pattern that resolves on a stale earlier response):
- utils/searchRBAC.ts searchForEntityShouldWork[ShowNoResult]: register
  before press('Enter')
- SearchIndexNestedColumns.spec.ts: predicate-match the URL that
  contains the encoded search term (the empty-query suggestion fetch
  was resolving the listener first)

Locator-stability / timeout fixes:
- Roles.spec.ts (×7 sites): replace strict-mode-violating
  expect(loader).not.toBeVisible() with waitForAllLoadersToDisappear
  (handles N loaders correctly)
- ExploreBrowse.spec.ts: explicit toBeVisible before clicking
  explore-tree-title-mysql so the tree settles after expandTreeNode
- Table.spec.ts: wait for sort + next-page responses, replace
  count()===15 with toHaveCount(15) auto-retry

Backend-propagation timeouts (the UI fetches lag the API write):
- utils/searchRBAC.ts exploreTreeCategories: wrap visibility/absence
  checks in expect.toPass({ timeout: 20s }) — tree counts come from
  multiple aggregation queries, single search-query wait is not enough
- utils/searchRBAC.ts exploreShouldShowEntity: bump negative-assertion
  to 45s — RBAC filtering against newly-assigned roles lags the patch
  by several seconds
- utils/taskWorkflow.ts openTaskDetails: 15s → 45s — activity-feed
  refresh after API task create lags past default under load

Removed 7 unannotated `waitForTimeout` calls that were leaking past
ESLint and replaced with proper waits where applicable (Tasks/, Pages/
TaskComments + TasksUIFlow, ActivityStream, ActivityFeed). The 4 in
DomainUIInteractions / DataProductAndSubdomains were migrated to the
existing `waitForSearchIndexed` polling helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chirag-madlani chirag-madlani requested a review from a team as a code owner June 29, 2026 16:01
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions github-actions Bot added safe to test Add this label to run secure Github workflows on PRs UI UI specific issues labels Jun 29, 2026
Comment thread openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BulkImport.spec.ts Outdated
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🔴 Playwright Results — 10 failure(s), 57 flaky

✅ 4416 passed · ❌ 10 failed · 🟡 57 flaky · ⏭️ 38 skipped

Shard Passed Failed Flaky Skipped
🔴 Shard 1 373 8 5 11
🔴 Shard 2 803 2 11 9
🟡 Shard 3 818 0 2 7
🟡 Shard 4 809 0 4 10
🟡 Shard 5 837 0 27 0
🟡 Shard 6 776 0 8 1

Genuine Failures (failed on all attempts)

Pages/Lineage/LineageInteraction.spec.ts › Verify edge click opens edge drawer (shard 1)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator: getByTestId('edge-pw-database-service-3845510e.pw-database-c79fd9cb.pw-database-schema-72cd3b87.pw-table-ea80a25e-713e-4664-994c-957e4be15ee7-undefined')
Expected: visible
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for getByTestId('edge-pw-database-service-3845510e.pw-database-c79fd9cb.pw-database-schema-72cd3b87.pw-table-ea80a25e-713e-4664-994c-957e4be15ee7-undefined')�[22m

Pages/Lineage/LineageInteraction.spec.ts › Verify edge delete button in drawer (shard 1)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator: getByTestId('edge-pw-database-service-85584b34.pw-database-ab743d12.pw-database-schema-06d545c2.pw-table-53a63491-6905-4bb6-a3d6-ebc0301b53e6-undefined')
Expected: visible
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for getByTestId('edge-pw-database-service-85584b34.pw-database-ab743d12.pw-database-schema-06d545c2.pw-table-53a63491-6905-4bb6-a3d6-ebc0301b53e6-undefined')�[22m

Pages/Lineage/LineageInteraction.spec.ts › Verify node panel opens on click (shard 1)
�[31mTest timeout of 60000ms exceeded.�[39m
Pages/Lineage/LineageInteraction.spec.ts › Verify edit mode with edge operations (shard 1)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator: getByTestId('edge-pw-database-service-ddd2d9fa.pw-database-cf5c0717.pw-database-schema-508e58c0.pw-table-b7d8ff93-2b50-4a9f-829a-de9588163f54-undefined')
Expected: visible
Timeout: 15000ms
Error: element(s) not found

Call log:
�[2m  - Expect "toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for getByTestId('edge-pw-database-service-ddd2d9fa.pw-database-cf5c0717.pw-database-schema-508e58c0.pw-table-b7d8ff93-2b50-4a9f-829a-de9588163f54-undefined')�[22m

Pages/Lineage/PlatformLineage.spec.ts › Verify table search with special characters as handled (shard 1)
TimeoutError: page.waitForResponse: Timeout 30000ms exceeded while waiting for event "response"
Pages/Lineage/PlatformLineage.spec.ts › Verify service platform view (shard 1)
TimeoutError: page.waitForResponse: Timeout 30000ms exceeded while waiting for event "response"
Pages/Lineage/PlatformLineage.spec.ts › Verify domain platform view (shard 1)
TimeoutError: page.waitForResponse: Timeout 30000ms exceeded while waiting for event "response"
Pages/Lineage/PlatformLineage.spec.ts › Verify platform view switching (shard 1)
TimeoutError: page.waitForResponse: Timeout 30000ms exceeded while waiting for event "response"
Features/BulkEditEntity.spec.ts › Database Schema (shard 2)
Error: Unable to fill the active grid description editor
Features/BulkEditEntity.spec.ts › Glossary (shard 2)
Error: Unable to select grid column "name"
🟡 57 flaky test(s) (passed on retry)
  • Features/DataAssetRulesEnabled.spec.ts › Verify the Directory Entity Action items after rules is Enabled (shard 1, 1 retry)
  • Pages/Lineage/DataAssetLineage.spec.ts › verify create lineage for entity - Mlmodel (shard 1, 1 retry)
  • Flow/SearchRBAC.spec.ts › User without permission (shard 1, 1 retry)
  • Flow/SearchRBAC.spec.ts › User without permission (shard 1, 1 retry)
  • Flow/SearchRBAC.spec.ts › a fully denied user sees neither asset type when browsing (shard 1, 1 retry)
  • Features/BulkEditEntity.spec.ts › Database (shard 2, 1 retry)
  • Features/BulkEditEntity.spec.ts › Table (shard 2, 2 retries)
  • Features/BulkImport.spec.ts › Database service (shard 2, 2 retries)
  • Features/BulkImport.spec.ts › Database Schema (shard 2, 1 retry)
  • Features/ContextCenterPermission.spec.ts › user with deleteAll permission can see delete action but not restore action on an archived document, and can delete it (shard 2, 1 retry)
  • Features/ContextCenterPermission.spec.ts › user with deleteAll permission sees no row delete action on memories they do not own, but can delete their own memory from the modal (shard 2, 1 retry)
  • Features/DataQuality/TestCaseImportExportE2eFlow.spec.ts › Admin: Complete export-import-validate flow (shard 2, 2 retries)
  • Features/ExploreFilterComposition.spec.ts › glossary term filter spans asset types and ANDs with tier (shard 2, 1 retry)
  • Features/ExploreQuickFilters.spec.ts › should persist quick filter on global search (shard 2, 1 retry)
  • Features/GlobalPageSize.spec.ts › Page size should persist across different pages (shard 2, 1 retry)
  • Features/Glossary/GlossaryAdvancedOperations.spec.ts › should create glossary with multiple owners (users + teams) (shard 2, 1 retry)
  • Features/RestoreEntityInheritedFields.spec.ts › Validate restore with Inherited domain and data products assigned (shard 3, 1 retry)
  • Features/SearchExport.spec.ts › Export queues a background job and downloads from the jobs tray (shard 3, 1 retry)
  • Flow/NestedChildrenUpdates.spec.ts › should update nested column description immediately without page refresh (shard 4, 1 retry)
  • Flow/PersonaFlow.spec.ts › Set default persona for team should work properly (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Entity Reference (shard 4, 1 retry)
  • Pages/DataContractInheritance.spec.ts › Full Contract Inheritance - Asset inherits full contract from Data Product (shard 4, 1 retry)
  • Pages/Domains.spec.ts › Rename domain with tags and glossary terms preserves associations (shard 5, 1 retry)
  • Pages/Entity.spec.ts › Domain Add, Update and Remove (shard 5, 1 retry)
  • Pages/Entity.spec.ts › User as Owner Add, Update and Remove (shard 5, 1 retry)
  • Pages/Entity.spec.ts › Team as Owner Add, Update and Remove (shard 5, 1 retry)
  • Pages/Entity.spec.ts › Certification Add Remove (shard 5, 1 retry)
  • Pages/Entity.spec.ts › User as Owner Add, Update and Remove (shard 5, 1 retry)
  • Pages/Entity.spec.ts › Domain Propagation (shard 5, 1 retry)
  • Pages/Entity.spec.ts › User should be denied access to edit description when deny policy rule is applied on an entity (shard 5, 1 retry)
  • ... and 27 more

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

karanh37
karanh37 previously approved these changes Jun 30, 2026
chirag-madlani and others added 2 commits June 30, 2026 19:44
An empty FQN encoded into the q= parameter becomes a match-all query,
which lets the helper return on the first poll against any non-empty
index — silently bypassing the very indexing race it exists to close.
Throw a clear error at the source instead, and drop the misleading
`?? ''` fallback at call sites so the type contract reflects reality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 2 resolved / 4 findings

Hardens E2E tests against ES indexing races and locator timeouts by implementing robust polling and deterministic response handling. Address the remaining waitForResponse race in exploreShouldShowEntity where the listener is registered prematurely before input completion.

💡 Quality: Closed-tab verification can silently no-op after reload

📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TasksUIFlow.spec.ts:458-472

The 'Resolve task and verify it moves to Closed' step previously called navigateToActivityFeedTasks(page) after page.reload(), guaranteeing the test landed on the Tasks/Activity-feed view before asserting. This commit replaces that navigation with only waitForPageLoaded(page). The subsequent verification is wrapped in if (await closedTab.isVisible()) { ... }. If, after reload, the page no longer renders the Tasks view (e.g. the URL/route does not restore the tab), closedTab will not be visible and the entire verification block — including the expect(closedTaskCard).toBeVisible() assertion — is skipped, so the step passes without verifying the task actually moved to Closed. This turns a meaningful assertion into a potential no-op. Consider re-asserting that the Tasks view (or Closed tab) is present after reload, or removing the conditional guard so the assertion is enforced.

Re-navigate to the tasks tab after reload and assert the Closed tab is present rather than conditionally skipping verification.
await page.reload();
await waitForPageLoaded(page);
await navigateToActivityFeedTasks(page);

const closedTab = page.getByRole('tab', { name: /Closed/i });
await expect(closedTab).toBeVisible();
await closedTab.click();
💡 Edge Case: openEntityTasksTab drops the button-variant Tasks tab fallback

📄 openmetadata-ui/src/main/resources/ui/playwright/utils/taskWorkflow.ts:357-371

The refactor of openEntityTasksTab removes the previous branch that handled a button-role Tasks tab (page.getByRole('button', { name: /tasks/i })) as an alternative to the menuitem-role tab, and also removes the isVisible() guards. The function now unconditionally does menuItemTaskTab.waitFor({ state: 'visible' }) and clicks it. If any entity page renders the Tasks tab as a button rather than a menuitem (which the old code explicitly accounted for), waitFor will hang until timeout and fail the test. If the menuitem variant is now the only one rendered across all entity types this is safe; otherwise this is a regression in robustness. Confirm the button variant no longer exists in the UI, or retain the fallback.

Preserve support for both the menuitem and button Tasks-tab variants.
const menuItemTaskTab = page.getByRole('menuitem', { name: /tasks/i });
const buttonTaskTab = page.getByRole('button', { name: /tasks/i });
const taskTab = (await menuItemTaskTab.isVisible().catch(() => false))
  ? menuItemTaskTab
  : buttonTaskTab;
await taskTab.waitFor({ state: 'visible' });
const taskListResponse = waitForTaskListResponse(page);
await taskTab.click();
await taskListResponse.catch(() => undefined);
✅ 2 resolved
Quality: Unused escape import added to SearchIndexNestedColumns.spec.ts

📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/SearchIndexNestedColumns.spec.ts:17
This commit adds import { escape } from 'lodash'; (line 17), but escape is never referenced anywhere in the file — a Grep for escape returns only the import itself. The previous code in this commit removed the byNameResponse waitForResponse logic that presumably would have used it, but the import was left behind.

This is dead code and will be flagged by the project's ESLint (no-unused-vars/@typescript-eslint/no-unused-vars), which the review instructions require running (eslint --fix). It could cause a lint/CI failure or at minimum be auto-stripped by organize-imports:cli. Remove the unused import.

Edge Case: Empty-FQN fallback silently no-ops waitForSearchIndexed

📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BulkImport.spec.ts:808 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/TestCaseImportExportE2eFlow.spec.ts:79 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/TestCaseImportExportE2eFlow.spec.ts:150
Several new call sites pass a ?? '' fallback as the entity FQN to waitForSearchIndexed, e.g. tableEntity.entityResponseData.fullyQualifiedName ?? '' (BulkImport.spec.ts:808) and table.testCasesResponseData[0]?.fullyQualifiedName ?? '' (TestCaseImportExportE2eFlow.spec.ts:79,150).

waitForSearchIndexed builds the request as /api/v1/search/query?q=${encodeURIComponent(entityFqn)}&index=...&size=1 and returns as soon as hits.total.value > 0 (polling.ts:31-44). When entityFqn is the empty string, q= becomes a match-all query, so the very first poll returns hits as long as the index has any document — the function returns immediately without ever confirming the just-created entity is indexed. That silently defeats the exact race these waits are meant to close, and the test then proceeds to the flaky assertion the PR was trying to stabilize (export missing the new test case / empty bulk-edit grid), producing a confusing failure far from the root cause rather than a clear timeout here.

This only triggers if the FQN is actually undefined, but in that case failing fast with a clear message is far more debuggable than a silent bypass. Consider asserting the FQN is present (or have waitForSearchIndexed throw on an empty query) instead of substituting ''.

🤖 Prompt for agents
Code Review: Hardens E2E tests against ES indexing races and locator timeouts by implementing robust polling and deterministic response handling. Address the remaining `waitForResponse` race in `exploreShouldShowEntity` where the listener is registered prematurely before input completion.

1. 💡 Quality: Closed-tab verification can silently no-op after reload
   Files: openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TasksUIFlow.spec.ts:458-472

   The 'Resolve task and verify it moves to Closed' step previously called `navigateToActivityFeedTasks(page)` after `page.reload()`, guaranteeing the test landed on the Tasks/Activity-feed view before asserting. This commit replaces that navigation with only `waitForPageLoaded(page)`. The subsequent verification is wrapped in `if (await closedTab.isVisible()) { ... }`. If, after reload, the page no longer renders the Tasks view (e.g. the URL/route does not restore the tab), `closedTab` will not be visible and the entire verification block — including the `expect(closedTaskCard).toBeVisible()` assertion — is skipped, so the step passes without verifying the task actually moved to Closed. This turns a meaningful assertion into a potential no-op. Consider re-asserting that the Tasks view (or Closed tab) is present after reload, or removing the conditional guard so the assertion is enforced.

   Fix (Re-navigate to the tasks tab after reload and assert the Closed tab is present rather than conditionally skipping verification.):
   await page.reload();
   await waitForPageLoaded(page);
   await navigateToActivityFeedTasks(page);
   
   const closedTab = page.getByRole('tab', { name: /Closed/i });
   await expect(closedTab).toBeVisible();
   await closedTab.click();

2. 💡 Edge Case: openEntityTasksTab drops the button-variant Tasks tab fallback
   Files: openmetadata-ui/src/main/resources/ui/playwright/utils/taskWorkflow.ts:357-371

   The refactor of `openEntityTasksTab` removes the previous branch that handled a `button`-role Tasks tab (`page.getByRole('button', { name: /tasks/i })`) as an alternative to the `menuitem`-role tab, and also removes the `isVisible()` guards. The function now unconditionally does `menuItemTaskTab.waitFor({ state: 'visible' })` and clicks it. If any entity page renders the Tasks tab as a button rather than a menuitem (which the old code explicitly accounted for), `waitFor` will hang until timeout and fail the test. If the menuitem variant is now the only one rendered across all entity types this is safe; otherwise this is a regression in robustness. Confirm the button variant no longer exists in the UI, or retain the fallback.

   Fix (Preserve support for both the menuitem and button Tasks-tab variants.):
   const menuItemTaskTab = page.getByRole('menuitem', { name: /tasks/i });
   const buttonTaskTab = page.getByRole('button', { name: /tasks/i });
   const taskTab = (await menuItemTaskTab.isVisible().catch(() => false))
     ? menuItemTaskTab
     : buttonTaskTab;
   await taskTab.waitFor({ state: 'visible' });
   const taskListResponse = waitForTaskListResponse(page);
   await taskTab.click();
   await taskListResponse.catch(() => undefined);

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants